﻿using System;
using System.Collections.Generic;
using System.Linq;
using StockholmsStad.Data.Context;
using StockholmsStad.Data.ContractFactory;
using StockholmsStad.Data.Entity;
using StockholmsStad.Data.Interfaces;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Utilities.Enums;
using StockholmsStad.Utilities.Exceptions;

namespace StockholmsStad.Data.DatabaseAgents
{
    internal class LanguageAgent : DBAgentBase, ILanguageAgent
    {
        #region ILanguageAgent Members

        public int AddLanguage(IDataContext context, string language)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                var ceremonyFactory = FactoryAgent.GetCeremonyFactory();
                var entityLanguage = ceremonyFactory.CreateEntityLanguage(language);
                dbCtx.Languages.InsertOnSubmit(entityLanguage);
                dbCtx.SubmitChanges();
                return entityLanguage.LanguageId;
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public void DeleteLanguage(IDataContext context, ValueIdPair language)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                // Tar bort dom som har önskat språket
                var ceremonies = dbCtx.Ceremonies.Where(c => c.RequestedLanguageId == language.Id).ToList();

                foreach (var ceremony in ceremonies)
                {
                    ceremony.RequestedLanguageId = null;
                }

                // ta bort språket från förrätare
                dbCtx.MinisterLanguages.DeleteAllOnSubmit(
                    dbCtx.MinisterLanguages.Where(ml => ml.LanguageId == language.Id));

                var languageEntity = dbCtx.Languages.Where(l => l.LanguageId == language.Id).SingleOrDefault();
                dbCtx.Languages.DeleteOnSubmit(languageEntity);
                dbCtx.SubmitChanges();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public void UpdateLanguage(IDataContext context, ValueIdPair language)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var languageEntity = dbCtx.Languages.Where(l => l.LanguageId == language.Id).SingleOrDefault();
                languageEntity.Name = language.Name;
                dbCtx.SubmitChanges();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public void DeleteCeremonyLanguage(IDataContext context, ValueIdPair language)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                int entityLanguageId = GetPrimaryKeyForCeremonyLanguage(context, language.Name);
                int swedishLanguageId = GetPrimaryKeyForCeremonyLanguage(context, "Svenska");
                var ceremonies = dbCtx.Ceremonies.Where(c => c.CeremonyLanguageId == entityLanguageId).ToList();

                foreach (var ceremony in ceremonies)
                {
                    ceremony.CeremonyLanguageId = swedishLanguageId; //Svenska
                    ceremony.Interpreter = true; //Tolk
                }

                var languageEntity = dbCtx.CeremonyLanguages.Where(l => l.CeremonyLanguageId == entityLanguageId).SingleOrDefault();
                dbCtx.CeremonyLanguages.DeleteOnSubmit(languageEntity);
                dbCtx.SubmitChanges();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public void DeleteCertificateLanguage(IDataContext context, ValueIdPair language)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                int entityLanguageId = GetPrimaryKeyForCertificateLanguage(context, language.Name);
                int englishLanguageId = GetPrimaryKeyForCertificateLanguage(context, "Engelska");
                var ceremonies = dbCtx.Ceremonies.Where(c => c.CertificateLanguageId == entityLanguageId).ToList();

                foreach (var ceremony in ceremonies)
                {
                    ceremony.CertificateLanguageId = englishLanguageId; //Engelska
                }

                var languageEntity = dbCtx.CertificateLanguages.Where(l => l.CertificateLanguageId == entityLanguageId).SingleOrDefault();
                dbCtx.CertificateLanguages.DeleteOnSubmit(languageEntity);
                dbCtx.SubmitChanges();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        public ValueIdPair GetLanguage(IDataContext context, string name)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Language language = SelectAll<Language>(dbCtx).Find(l => l.Name.Equals(name));
                if (language != null)
                {
                    return FactoryAgent.GetCeremonyFactory().CreateContractObject(language);
                }
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }

            return null;
        }

        public ValueIdPair GetLanguage(IDataContext context, int languadeId)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                Language language = SelectAll<Language>(dbCtx).Find(l => l.LanguageId == languadeId);
                
                if (language != null)
                    return FactoryAgent.GetCeremonyFactory().CreateContractObject(language);
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }

            return null;            
        }

        public List<ValueIdPair> GetLanguages(IDataContext context)
        {
            CeremonyFactory ceremonyFactory = FactoryAgent.GetCeremonyFactory();
            try
            {

                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                return dbCtx.Languages.Select(l => ceremonyFactory.CreateContractObject(l)).ToList();
            }
            catch (Exception ex)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, ex);
            }
        }

        #endregion

        private int GetPrimaryKeyForCeremonyLanguage(IDataContext context, string languageName)
        {
            ILanguageAgent agent = AgentFactory.GetLanguageAgent();
            ValueIdPair language = agent.GetLanguage(context, languageName);

            if (language == null)
                throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

            CeremonyLanguage lang;

            // Borde finnas i minnet
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                lang = SelectAll<CeremonyLanguage>(dbCtx).Find(cl => cl.LanguageId == language.Id);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }

            if (lang == null)
                throw new StockholmStadSystemException(SystemErrorCode.CeremonyLanguageDoesNotExist);

            return lang.CeremonyLanguageId;
        }

        private int GetPrimaryKeyForCertificateLanguage(IDataContext context, string languageName)
        {
            ILanguageAgent agent = AgentFactory.GetLanguageAgent();
            ValueIdPair language = agent.GetLanguage(context, languageName);

            if (language == null)
                throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

            CertificateLanguage lang;

            // Borde finnas i minnet
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                lang = SelectAll<CertificateLanguage>(dbCtx).Find(cl => cl.LanguageId == language.Id);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }

            if (lang == null)
                throw new StockholmStadSystemException(SystemErrorCode.CeremonyLanguageDoesNotExist);

            return lang.CertificateLanguageId;
        }

        public ValueIdPair GetDefaultLanguage(IDataContext context)
        {
            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();

                // TODO: Default språk, ska man speca det i konfig-fil? 
                var language = dbCtx.Languages.Where(l => l.LanguageId == 1).SingleOrDefault();

                if (language == null)
                    throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

                return new ValueIdPair { Name = language.Name, Id = language.LanguageId };
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public List<ValueIdPair> GetCertificateLanguages(IDataContext context)
        {
            var certificateLanguage = new List<ValueIdPair>();

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                List<CertificateLanguage> languages = SelectAll<CertificateLanguage>(dbCtx);

                foreach (CertificateLanguage lang in languages)
                {
                    certificateLanguage.Add(new ValueIdPair
                    {
                        Id = lang.CertificateLanguageId,
                        Name = lang.Language.Name
                    });
                }


                return certificateLanguage;
            }
            catch (Exception)
            {
                return new List<ValueIdPair>();
            }
        }

        public List<ValueIdPair> GetCeremonyLanguages(IDataContext context)
        {
            var ceremonyLanguages = new List<ValueIdPair>();

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                List<CeremonyLanguage> languages = SelectAll<CeremonyLanguage>(dbCtx);

                foreach (CeremonyLanguage lang in languages)
                {
                    ceremonyLanguages.Add(new ValueIdPair
                    {
                        Id = lang.CeremonyLanguageId,
                        Description = lang.Language.Name,
                        Name = lang.Language.Name
                    });
                }


                return ceremonyLanguages;
            }
            catch (Exception)
            {
                return new List<ValueIdPair>();
            }
        }

        public int AddCeremonyLanguage(IDataContext context, string languageName)
        {
            ILanguageAgent agent = AgentFactory.GetLanguageAgent();
            ValueIdPair language = agent.GetLanguage(context, languageName);

            if (language == null)
                throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                CeremonyLanguage lang =
                    FactoryAgent.GetCeremonyFactory().CreateEntityCeremonyLanguage(language.Id);
                return Insert(lang, dbCtx);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e);
            }
        }

        public int AddCertificateLanguage(IDataContext context, string languageName)
        {
            ILanguageAgent agent = AgentFactory.GetLanguageAgent();
            ValueIdPair language = agent.GetLanguage(context, languageName);
            // Language exist in global language table
            if (language == null)
                throw new StockholmStadSystemException(SystemErrorCode.LanguageDoesNotExist);

            try
            {
                StockholmsStadDataContext dbCtx = context.GetInternalContext();
                var lang = new CertificateLanguage { LanguageId = language.Id };
                return Insert(lang, dbCtx);
            }
            catch (Exception e)
            {
                throw new StockholmStadSystemException(SystemErrorCode.DataAccessFailure, e.InnerException);
            }
        }

    }
}